package com.tsq.security.auth.base;

import cn.hutool.crypto.Mode;
import cn.hutool.crypto.Padding;
import cn.hutool.crypto.digest.DigestUtil;
import cn.hutool.crypto.digest.HMac;
import cn.hutool.crypto.digest.HmacAlgorithm;
import cn.hutool.crypto.symmetric.AES;
import cn.hutool.http.HttpRequest;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.ImmutableMap;
import com.tsq.security.auth.service.UserServiceDetailImpl;
import org.apache.commons.lang3.StringUtils;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.OAuth2RequestFactory;
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;

import java.nio.charset.StandardCharsets;
import java.util.Map;

/**
 * 闪验一键登录授权
 * https://shanyan.253.com/document
 *
 * @author Tango
 * @since 2020/9/8
 */
public class ShanyanTokenGranter extends PasswordFreeGranter {
    private static final String GRANT_TYPE = "shanyan";
    private static final String url = "https://api.253.com/open/flashsdk/mobile-query";

    private UserServiceDetailImpl userDetailsService;

    public ShanyanTokenGranter(UserDetailsService userDetailsService,
                               AuthorizationServerTokenServices tokenServices,
                               ClientDetailsService clientDetailsService,
                               OAuth2RequestFactory requestFactory) {
        super(tokenServices, clientDetailsService, requestFactory, GRANT_TYPE);
        this.userDetailsService = (UserServiceDetailImpl) userDetailsService;
    }

    @Override
    protected Authentication parameterGrant(Map<String, String> parameters) {
        String appId = parameters.get("appId"),
                appKey = parameters.get("appKey"),
                token = parameters.get("token");
        if (StringUtils.isAnyBlank(appId, appKey, token)) {
            return fail("参数错误");
        }
        //签名算法：hmacSHA256(所有传入参数按字段名正序排序后拼接的字符串，应用appKey)
        byte[] key = appKey.getBytes(StandardCharsets.UTF_8);
        HMac mac = new HMac(HmacAlgorithm.HmacSHA256, key);
        String sign = mac.digestHex(appId + token);

        String body = HttpRequest.post(url)
                .form(ImmutableMap.of("appId", appId, "token", token, "sign", sign))
                .execute()
                .body();
        JSONObject json = JSON.parseObject(body);

        // 如在控制台创建应用时如填写了RSA公钥，则可以选择使用RSA算法解密，推荐1024或2048位PKCS#8格式密钥对。
        // 如未填写则只能使用AES CBC算法，以md5(appKey)前16位字符串为秘钥，后16位字符为初始化向量解密。
        if ("200000".equals(json.getString("code"))) {
            try {
                String md5 = DigestUtil.md5Hex(appKey);
                // AES/CBC/PKCS5Padding
                AES aes = new AES(Mode.CBC, Padding.PKCS5Padding,
                        md5.substring(0, 16).getBytes(StandardCharsets.UTF_8),
                        md5.substring(16).getBytes(StandardCharsets.UTF_8));

                String username = new String(aes.decrypt(json.getString("code")));
                UserDetails userDetails = getUser(username);
                return new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
            } catch (Exception e) {
                return fail("解密手机号失败: " + e.getMessage());
            }
        } else {
            return fail("闪验接口错误: " + json.getString("message"));
        }
    }


    @Override
    @SuppressWarnings("unchecked")
    protected UserServiceDetailImpl getUserDetailsServiceBean() {
        return userDetailsService;
    }
}
